/*
 * @file     g_adc.c
 * @brief    This file provides all the ADC firmware functions.
 *
 * Change Logs:
 * Date            Author             Version        Notes
 * 2021-08-25      xubo               V1.0.0         the first version
 * 2022-02-25      xubo               V1.0.1         add adc calculate code
 */

#include "g_adc.h"
#include "g_otp.h"
#include "g_sysctrl.h"
#define ADC_WAIT_TIME 10

static int calculate_voltage(int result, int vol_high_ref, int vol_low_ref, int vol_high, int vol_low);
static uint8_t r_otp_ref_para[16];
static G_ADC_ChxTypeDef ADC_Ch = ADC_GPIO_CHANNEL_0;

static void ADC_calibration()
{
    uint16_t VOL_Low_Ref, VOL_Low, ref_voltage_high, voltage_high = 0;
    double VOL_Slope = 0;
    VOL_Low_Ref = 0;
    ref_voltage_high = 0;
    uint8_t getmode = ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_MODE;
    uint8_t getscale = ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_GC;
    switch (getmode)
    {
    case ADC_GPIO:
        if (getscale == ADC_Scale_1200Mv)
        {

            VOL_Low_Ref = (r_otp_ref_para[0] | (r_otp_ref_para[1] << 8));
            ref_voltage_high = (r_otp_ref_para[2] | (r_otp_ref_para[3] << 8));
            VOL_Low = IO_LOW_VOLTAGE_REF;
            voltage_high = IO_HIHG_VOLTAGE_REF;
        }
        if (getscale == ADC_Scale_2400Mv)
        {
            VOL_Low_Ref = (r_otp_ref_para[4] | (r_otp_ref_para[5] << 8));
            ref_voltage_high = (r_otp_ref_para[6] | (r_otp_ref_para[7] << 8));
            VOL_Low = IO_3_3_LOW_VOLTAGE_REF;
            voltage_high = IO_3_3_HIGH_VOLTAGE_REF;
        }
        break;

    case ADC_HVIN:
        VOL_Low_Ref = (r_otp_ref_para[8] | (r_otp_ref_para[9] << 8));
        ref_voltage_high = (r_otp_ref_para[10] | (r_otp_ref_para[11] << 8));
        VOL_Low = HVIN_LOW_VOLTAGE_REF - 1000;
        voltage_high = HVIN_HIHG_VOLTAGE_REF - 1000;
        break;

    case ADC_VINLPM:
        VOL_Low_Ref = (r_otp_ref_para[12] | (r_otp_ref_para[13] << 8));
        ref_voltage_high = (r_otp_ref_para[14] | (r_otp_ref_para[15] << 8));
        VOL_Low = VINLPM_LOW_VOLTAGE_REF;
        voltage_high = VINLPM_HIHG_VOLTAGE_REF;

        break;
    default:
        return;
    }
    VOL_Slope = ((voltage_high - VOL_Low) / (ref_voltage_high - VOL_Low_Ref)) * 64;
    MADC->REF_AD.reg = VOL_Low_Ref;
    MADC->REF_VOL.reg = VOL_Low;
    MADC->SLOPE.reg = (uint16_t)VOL_Slope;
}

/**
 * @name   G_ADC_Init
 * @brief  ADC initialization setting.
 * @param  ADC_InitStruct :
 *         Enable configuration,
 *         Continuous sampling configuration,
 *         Voltage translation configuration,
 *         Mode configuration,
 *         Scale configuration.
 * @retval None
 */
void G_ADC_Init(G_ADC_InitTypeDef *ADC_InitStruct)
{
    _ASSERT(IS_FUNCTIONAL_STATE(ADC_InitStruct->EN));
    _ASSERT(IS_ADC_MODE(ADC_InitStruct->Mode));
    _ASSERT(IS_ADC_SCALE(ADC_InitStruct->Scale));
    _ASSERT(IS_FUNCTIONAL_STATE(ADC_InitStruct->ContModeEn));
    _ASSERT(IS_FUNCTIONAL_STATE(ADC_InitStruct->Volt_Trans_En));

    ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN_REG = ENABLE;
    ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN_CONSTGM = ENABLE;
    ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN_BIASGEN = ENABLE;
    ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_clk = 1; // 2;
    ADC_ANALOG->ADC_CTRL0.bit.MISC_GBG_EN = DISABLE;
    ADC_ANALOG->ADC_CTRL0.bit.MISC_GBG_FASTSETTLING = DISABLE;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBC_IBUF = 7;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBC_CMBUF = 1;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBC_REFBUF = 7;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBC_REFBUF2 = 0;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_BW = 3;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_EN_RC = 1;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_REFBUF_VREF_CTRL = 2;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_REGA_VCTRL = 3;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_REGD_VCTRL = 3;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_VCTRL_BIASGEN = 6;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_GC = ADC_InitStruct->Scale;
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_SEL_CH_S = ADC_Ch;

    MADC->CTRL2.bit.SAMP_MODE = ADC_InitStruct->ContModeEn;
    MADC->CONVERT_CTRL.bit.EN = ADC_InitStruct->Volt_Trans_En;
    MADC->FIFO_CTRL.bit.EN = ENABLE;
    *(volatile uint8_t *)(0xf853c) = 0x88;
    G_OTP_Read(ADC_REF_OTP_ADDR, r_otp_ref_para, 16);
    uint8_t vddlpmbuf[4] = {0};
    G_OTP_Read(ADC_REF_OTP_ADDR + 16 + 2, vddlpmbuf, 4);
    if (vddlpmbuf[1] == 0x2 && vddlpmbuf[3] == 0x2)
    {
        memcpy(r_otp_ref_para + 12, vddlpmbuf, 4);
    }
    G_ADC_ModeConfig(ADC_InitStruct->Mode, ADC_InitStruct->Scale);
    MADC->CTRL1.bit.EN = ADC_InitStruct->EN;
    ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN = ADC_InitStruct->EN;
}

/**
 * @name   G_ADC_GetChannel
 * @brief  Software trigger adc sampling.
 * @param  None
 * @retval None
 */
G_ADC_ChxTypeDef G_ADC_GetChannel(void)
{
    return (G_ADC_ChxTypeDef)ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_SEL_CH_S;
}

/**
 * @name G_ADC_GetResult
 * @brief  Get the ADC voltage.
 * @param  ADC_Chx :Channel configuration
 * @retval The ADC data.
 */
uint16_t G_ADC_GetResult(G_ADC_ChxTypeDef ADC_Chx)
{
    _ASSERT(IS_ADC_CHANNEL(ADC_Chx));
    uint8_t getmode = ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_MODE;
    if (ADC_Ch != ADC_Chx)
    {
        ADC_Ch = ADC_Chx;
        ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_SEL_CH_S = ADC_Chx;
        ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN = DISABLE;
        ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN = ENABLE;
        delay_us(ADC_WAIT_TIME);
    }
    if (getmode == ADC_HVIN)
    {
        if (MADC->CONVERT_CTRL.bit.EN)
        {
            return MADC->DATA.reg + 1000;
        }
        else
        {
            return MADC->DATA.reg;
        }
    }
    return MADC->DATA.reg;
}

/**
 * @name G_ADC_GetFIFOResult
 * @brief  Get the voltage in adc fifo.
 * @param  ADC_Chx :Channel configuration.
 * @param  buff    :Buff first address.
 * @param  BuffLen :Buff length,max val can be 16.
 * @retval None
 */
void G_ADC_GetFIFOResult(G_ADC_ChxTypeDef ADC_Chx, uint16_t *buff, uint8_t BuffLen)
{
    _ASSERT(IS_ADC_CHANNEL(ADC_Chx));
    uint8_t Num = 0;
    for (Num = 0; Num < BuffLen; Num++)
    {
        buff[Num] = MADC->FIFO.reg;
    }
}

/**
 * @name G_ADC_GetFIFOCount
 * @brief  Get the ADC_FIFO data length.
 * @retval MADC->FIFO_ITEMS: ADC_FIFO data length.
 */
uint8_t G_ADC_GetFIFOCount(void)
{
    return MADC->FIFO_CTRL.bit.ITEMS;
}

/**
 * @name G_ADC_FifoReset
 * @brief  Reset the adc fifo,this can clear adc fifo.
 * @retval None
 */
void G_ADC_FifoReset(void)
{
    MADC->FIFO_CTRL.bit.RST = SET;
    MADC->FIFO_CTRL.bit.RST = RESET;
}

/**
 * @name   G_ADC_IRQCmd
 * @brief  Enable adc irq.
 * @param  irqn: Interrupt flag bit: This param can be EOC,FIFO_HI,FIFO_OVFL,AWD.
 * @param  NewState: this param can be ENABLE and DISABLE.
 * @retval None
 */
void G_ADC_IRQCmd(G_ADC_IE_TypeDef irqn, G_FunctionalState NewState)
{
    _ASSERT(IS_ADC_IE(irqn));
    if (NewState)
    {
        MADC->IE.reg |= irqn;
    }
    else
    {
        MADC->IE.reg &= ~irqn;
    }
}

/**
 * @name   G_ADC_Volt_TransEn
 * @brief  ADC voltage translation start control.
 * @param  NewState: ENABLE OR DISABLE
 * @retval None
 */
void G_ADC_Volt_TransEn(G_FunctionalState NewState)
{
    MADC->CONVERT_CTRL.bit.EN = NewState;
}

/**
 * @name   G_ADC_TriggerOneSample
 * @brief  Software trigger adc sampling.
 * @param  None
 * @retval None
 */
void G_ADC_TriggerOneSample(void)
{
    MADC->CTRL2.bit.SGL_START = ENABLE;
}

/**
 * @name   G_ADC_WaterLevelSet
 * @brief  ADC WaterLevel interrupt configuration.
 * @param  level: Water level threshold: this param can be 0:15.
 * @retval None
 */
void G_ADC_WaterLevelSet(uint8_t level)
{
    MADC->FIFO_CTRL.bit.LIMIT = level;
}

/**
 * @name   G_ADC_ContModeEn
 * @brief  ADC sampling mode configuration
 * @param  NewState: ENABLE:Continuous sampling mode,
 *                   DISABLE:Single sampling mode.
 * @retval None
 */
void G_ADC_ContModeEn(G_FunctionalState NewState)
{
    _ASSERT(IS_FUNCTIONAL_STATE(NewState));
    MADC->CTRL2.bit.SAMP_MODE = NewState;
}

/**
 * @name   G_ADC_AwdEn
 * @brief  Enable or disable adc watch dog.
 * @param  NewState: ENABLE:Continuous sampling mode,
 *                   DISABLE:Single sampling mode.
 * @retval None
 */
void G_ADC_AwdEn(G_FunctionalState Newstate)
{
    _ASSERT(IS_FUNCTIONAL_STATE(Newstate));
    MADC->CTRL1.bit.AWD_EN = Newstate;
}

/**
 * @name   G_ADC_TimerSelect
 * @brief  Choose which timer to trigger adc sampling.
 * @param  ADC_TmrSt: Timer trigger selection:This param can be TIM0:TIM8.
 * @retval None
 */
void G_ADC_TimerSelect(G_TIM_NumTypeDef ADC_TmrSt)
{
    _ASSERT(IS_TIMER_NUM(ADC_TmrSt));
    MADC->CTRL2.bit.TRIG_EN = BIT0 << ADC_TmrSt;
}

/**
 * @name   G_ADC_GetITStates
 * @brief  Get the ADC interrupt flag bit status.
 * @param  NewStates :Struct of G_ADC_IrqStatesTypeDef.
 * @retval Struct of G_ADC_IrqStatesTypeDef.
 */
void G_ADC_GetITStates(G_ADC_IrqStatesTypeDef *NewStates)
{
    NewStates->ADC_EocState = MADC->STATUS.bit.DONE;
    NewStates->ADC_FifoHiState = MADC->STATUS.bit.FIFO_FULL;
    NewStates->ADC_FifoOvflState = MADC->STATUS.bit.FIFO_OVER;
    NewStates->ADC_AwdState = MADC->STATUS.bit.AWD;
}

/**
 * @name   G_ADC_ClearITPending
 * @brief  ADC interrupt flag remove function.
 * @param  irqn: Interrupt flag bit: This param can be EOC,FIFO_HI,FIFO_OVFL,AWD.
 * @retval None
 */
void G_ADC_ClearITPending(G_ADC_IE_TypeDef irqn)
{
    _ASSERT(IS_ADC_IE(irqn));
    MADC->STATUS.reg = irqn;
}

/**
 * @name   G_ADC_AwdThresholdSet
 * @brief  ADC watchdog threshold configuration.
 * @param  Awd_hi :High threshold for ADC watchdog.
 * @param  Awd_lo :Low threshold for ADC watchdog.
 * @retval None
 */
void G_ADC_AwdThresholdSet(uint16_t Awd_hi, uint16_t Awd_lo)
{
    MADC->AWD.bit.HIGH_LIMIT = Awd_hi;
    MADC->AWD.bit.LOW_LIMIT = Awd_lo;
}

/**
 * @name   G_ADC_ModeConfig
 * @brief  ADC mode selection.
 * @param  Mode: struct of G_ADC_ModeTypeDef.
 * @param  Scale : struct of G_ADC_ScaleTypeDef.
 * @retval None
 */
void G_ADC_ModeConfig(G_ADC_ModeTypeDef Mode, G_ADC_ScaleTypeDef Scale)
{
    _ASSERT(IS_ADC_MODE(Mode));
    if (Mode == ADC_HVIN || Mode == ADC_VINLPM)
    {
        ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_MODE = Mode;
        ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_GC = ADC_Scale_700Mv;
        ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN = DISABLE;
        ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN = ENABLE;
    }
    else if (Mode == ADC_DIFF)
    {
        ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_GC = Scale;
        ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_MODE = Mode;
        ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN = DISABLE;
        ADC_ANALOG->ADC_CTRL0.bit.MISC_SARADC_EN = ENABLE;
    }
    else
    {
        ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_GC = Scale;
        ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_MODE = Mode;
    }
    delay_us(ADC_WAIT_TIME);
    ADC_calibration();
}

/**
 * @name   G_ADC_ScaleConfig
 * @brief  ADC Scale selection.
 * @param  ADC_Sacle: struct of G_ADC_ScaleTypeDef.
 * @retval None
 */
void G_ADC_ScaleConfig(G_ADC_ScaleTypeDef ADC_Sacle)
{
    _ASSERT(IS_ADC_SCALE(ADC_Sacle));
    ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_GC = ADC_Sacle;
}

static uint16_t ADC_GetAdResArray1(uint16_t *AdRes, int AdResLen)
{
    int i = 0;
    int j = 0;
    uint16_t temp = 0;
    int sum = 0;
    /*setp1 : sort*/
    for (i = 0; i < AdResLen; i++)
    {
        for (j = 0; j < AdResLen - i - 1; j++)
        {
            if (AdRes[j] > AdRes[j + 1])
            {
                temp = AdRes[j];
                AdRes[j] = AdRes[j + 1];
                AdRes[j + 1] = temp;
            }
        }
    }

    for (i = REMOVE_NUM; i < AdResLen - REMOVE_NUM; i++)
    {
        sum += AdRes[i];
    }
    temp = sum / (AdResLen - 2 * REMOVE_NUM);
    return temp;
}

/**
 * @name   G_ADC_GetVoltage
 * @brief  ADC Get Voltage by software.
 * @param  None.
 * @retval None
 */
int G_ADC_GetVoltage(G_ADC_ChxTypeDef ADC_Channel)
{
    int v_rang = 0;
    int result = 0;
    int voltage_low = 2800;
    int voltage_high = 5000;
    int reg_voltage_low = 640 * 1000;
    int ref_voltage_high = 744 * 1000;
    uint16_t adc_result_buf[FILT_NUM] = {0};

    uint8_t getmode = ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_MODE;
    uint8_t getscale = ADC_ANALOG->ADC_CTRL1.bit.MISC_SARADC_IBUF_GC;
    switch (getmode)
    {
    case ADC_GPIO:
        if (getscale == ADC_Scale_1200Mv)
        {

            reg_voltage_low = (r_otp_ref_para[0] | (r_otp_ref_para[1] << 8)) * 1000;
            ref_voltage_high = (r_otp_ref_para[2] | (r_otp_ref_para[3] << 8)) * 1000;
            voltage_low = IO_LOW_VOLTAGE_REF;
            voltage_high = IO_HIHG_VOLTAGE_REF;
            v_rang = 1200;
        }
        if (getscale == ADC_Scale_2400Mv)
        {
            reg_voltage_low = (r_otp_ref_para[4] | (r_otp_ref_para[5] << 8)) * 1000;
            ref_voltage_high = (r_otp_ref_para[6] | (r_otp_ref_para[7] << 8)) * 1000;
            voltage_low = IO_3_3_LOW_VOLTAGE_REF;
            voltage_high = IO_3_3_HIGH_VOLTAGE_REF;
            v_rang = 3300;
        }
        break;

    case ADC_HVIN:
        reg_voltage_low = (r_otp_ref_para[8] | (r_otp_ref_para[9] << 8)) * 1000;
        ref_voltage_high = (r_otp_ref_para[10] | (r_otp_ref_para[11] << 8)) * 1000;
        voltage_low = HVIN_LOW_VOLTAGE_REF;
        voltage_high = HVIN_HIHG_VOLTAGE_REF;
        v_rang = 5000;
        break;

    case ADC_VINLPM:
        reg_voltage_low = (r_otp_ref_para[12] | (r_otp_ref_para[13] << 8)) * 1000;
        ref_voltage_high = (r_otp_ref_para[14] | (r_otp_ref_para[15] << 8)) * 1000;
        voltage_low = VINLPM_LOW_VOLTAGE_REF;
        voltage_high = VINLPM_HIHG_VOLTAGE_REF;
        v_rang = 3300;
        break;

    default:
        return -1;
    }

    if (reg_voltage_low == 0 || ref_voltage_high == 0)
    {
        return -1;
    }

    for (uint8_t i = 0; i < FILT_NUM; i++)
    {
        adc_result_buf[i] = G_ADC_GetResult(ADC_Channel);
    }
    uint16_t ad = ADC_GetAdResArray1(adc_result_buf, FILT_NUM);
    if (ad == 0)
    {
        return -1;
    }
    result = calculate_voltage(ad, ref_voltage_high, reg_voltage_low, voltage_high, voltage_low);

    return result > v_rang ? v_rang : result;
}

/**
 * @brief  calculate voltage ,acording reference value and reference voltage
 * @param  result       : adc result
 *         vol_high_ref : high voltage reference value
 *         vol_low_ref  : low voltage reference value
 *         vol_high     : high reference voltage (mV)
 *         vol_low      : low reference voltage
 * @retval voltage (mV)
 */
static int calculate_voltage(int result, int vol_high_ref, int vol_low_ref, int vol_high, int vol_low)
{
    int rega = result * 1000 - vol_low_ref;
    int regb = vol_high_ref - vol_low_ref;
    int regc = vol_high - vol_low;
    int temp = (rega * regc) / regb + vol_low;
    return temp > 0 ? temp : (0 - temp);
}

/************************ (C) COPYRIGHT GKT *****END OF FILE****/
